package mqtt

import (
	"cloud-export-iotkit-mqtt/pkg/config"
	"cloud-export-iotkit-mqtt/pkg/core"
	"cloud-export-iotkit-mqtt/pkg/driver"
	"crypto/md5"
	"errors"
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/interfaces"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
	"time"
)

type IotKitDevice struct {
	ProductKey string
	DeviceName string
	Model      string
}

const QOS0 = byte(0)
const QOS1 = byte(1)
const QOS2 = byte(2)

type MqttClient struct {
	mqtt.Client
	lc         logger.LoggingClient
	Service    interfaces.ApplicationService
	mqttConfig config.MQTTConfig
	opts       *mqtt.ClientOptions
}

func (c *MqttClient) Initialize(logger *logger.LoggingClient, mqttConfig config.MQTTConfig, connectHandler driver.ConnectCallback, connectLostHandler driver.ConnectLostCallback) (err error) {
	c.mqttConfig = mqttConfig
	// todo: 封装方法

	c.opts = mqtt.NewClientOptions()
	c.opts.AddBroker(c.mqttConfig.BrokerAddress)

	c.opts.SetClientID(c.mqttConfig.ClientId)
	c.opts.SetAutoReconnect(c.mqttConfig.AutoReconnect)
	//authMode := c.mqttConfig.AuthMode
	c.opts.SetUsername(c.mqttConfig.DeviceName)
	pwd := genPwd(c.mqttConfig)
	c.opts.SetPassword(pwd)

	// 回调初始
	handler := mqtt.OnConnectHandler(func(client mqtt.Client) {
		// 在这里调用回调函数
		connectHandler(c)
	})
	c.opts.OnConnect = handler

	lostHandler := mqtt.ConnectionLostHandler(func(client mqtt.Client, err error) {
		connectLostHandler(c, err)
	})

	c.opts.OnConnectionLost = lostHandler
	return nil
}

func (c *MqttClient) Publish(topic string, qos byte, payload interface{}, timeout time.Duration) core.Result {
	c.lc.Infof("publish: %s, payload: %s, qos:%s", topic, payload, qos)
	token := c.Client.Publish(topic, qos, false, payload)
	result := core.NewResult()
	go func() {
		waitTimeout := token.WaitTimeout(timeout)
		if waitTimeout {
			result.SetError(errors.New("timeout"))
		} else {
			result.SetValue(token.Done())
		}
	}()
	return result
}

func (c *MqttClient) Subscribe(topic string, callback driver.MessageCallback) core.Result {
	// 将 MessageCallback 转换为 MessageHandler 类型
	handler := mqtt.MessageHandler(func(client mqtt.Client, message mqtt.Message) {
		// 在这里调用回调函数
		callback(message)
	})
	token := c.Client.Subscribe(topic, QOS0, handler)

	result := core.NewResult()
	go func() {
		ret := token.Wait()
		c.lc.Infof("subscribe:%s, result: %s", topic, ret)
		if ret {
			result.SetError(errors.New("timeout"))
		} else {
			result.SetValue(token.Done())
		}
	}()
	return result
}

func (c *MqttClient) Connect() {

	client := mqtt.NewClient(c.opts)
	//
	c.Client = client
	//

	token := client.Connect()
	token.Wait()
	// TODO: 异常
}

func (c *MqttClient) Destroy() error {
	//TODO implement me
	c.Client.Disconnect(0)
	return nil
}

func NewMqttClient(lc logger.LoggingClient,
	service interfaces.ApplicationService) driver.ProtocolServiceChanel {

	client := MqttClient{lc: lc, Service: service}
	return &client

}

func genPwd(mqttConfig config.MQTTConfig) string {
	password := mqttConfig.ProductKey + mqttConfig.ClientId
	sum := md5.Sum([]byte(password))
	pwd := string(sum[:])
	return pwd
}

func (c *MqttClient) subscribeTopicByFormat(strFmt string, productKey string, deviceName string, handler mqtt.MessageHandler, qos byte) bool {
	topic := fmt.Sprintf(strFmt, productKey, deviceName)
	getToken := c.Client.Subscribe(topic, qos, handler)

	if getToken.Wait() && getToken.Error() != nil {
		c.Client.Disconnect(0)
		c.lc.Infof("could not subscribe to topic '%s': %s",
			topic, getToken.Error().Error())
		return true
	}
	return false
}

func (c *MqttClient) onConnectLostHandler(client mqtt.Client, err error) {
	if err == nil {
		c.lc.Infof("应该是正常关闭吧")
		return
	}

	// TODO: 设置重连间隔
	client.Connect()
	c.lc.Infof("onConnectLostHandler. error: %v", err)
	return
}
